package com.baidu.iit.pxp.el.juel;

import com.baidu.iit.pxp.el.*;

import java.lang.reflect.Method;
import java.util.Collection;

/**
 * User: huangweili
 * Date: 14-4-29
 * Time: 下午2:18
 */
public class Tree {
    private final ExpressionNode root;
    private final Collection<FunctionNode> functions;
    private final Collection<IdentifierNode> identifiers;
    private final boolean deferred;

    public Tree(ExpressionNode root, Collection<FunctionNode> functions, Collection<IdentifierNode> identifiers, boolean deferred) {
        super();
        this.root = root;
        this.functions = functions;
        this.identifiers = identifiers;
        this.deferred = deferred;
    }

    public Iterable<FunctionNode> getFunctionNodes() {
        return functions;
    }

    public Iterable<IdentifierNode> getIdentifierNodes() {
        return identifiers;
    }


    public ExpressionNode getRoot() {
        return root;
    }

    public boolean isDeferred() {
        return deferred;
    }

    @Override
    public String toString() {
        return getRoot().getStructuralId(null);
    }


    public Bindings bind(FunctionMapper fnMapper, VariableMapper varMapper) {
        return bind(fnMapper, varMapper, null);
    }


    public Bindings bind(FunctionMapper fnMapper, VariableMapper varMapper, TypeConverter converter) {
        Method[] methods = null;
        if (!functions.isEmpty()) {
            if (fnMapper == null) {
                throw new ELException("error.function.nomapper");
            }
            methods = new Method[functions.size()];
            for (FunctionNode node : functions) {
                String image = node.getName();
                Method method = null;
                int colon = image.indexOf(':');
                if (colon < 0) {
                    method = fnMapper.resolveFunction("", image);
                } else {
                    method = fnMapper.resolveFunction(image.substring(0, colon), image.substring(colon + 1));
                }
                if (method == null) {
                    throw new ELException("error.function.notfound");
                }
                if (node.isVarArgs() && method.isVarArgs()) {
                    if (method.getParameterTypes().length > node.getParamCount() + 1) {
                        throw new ELException("error.function.params");
                    }
                } else {
                    if (method.getParameterTypes().length != node.getParamCount()) {
                        throw new ELException("error.function.params");
                    }
                }
                methods[node.getIndex()] = method;
            }
        }
        ValueExpression[] expressions = null;
        if (identifiers.size() > 0) {
            expressions = new ValueExpression[identifiers.size()];
            for (IdentifierNode node : identifiers) {
                ValueExpression expression = null;
                if (varMapper != null) {
                    expression = varMapper.resolveVariable(node.getName());
                }
                expressions[node.getIndex()] = expression;
            }
        }
        return new Bindings(methods, expressions, converter);
    }
}
